One of the fundamental goals of computer security is to ensure the integrity of system resources, including memory and systems in the memory, such as operating systems, applications, domain manager, etc. The kernel is a program that constitutes the central core of the operating system by assuming complete control over substantially all that occurs in such a system. The kernel itself does not interact directly with the user, but rather interacts with the shell and other programs as well as with the hardware devices in the system including the processor (CPU), memory and disk drives. The kernel provides basic services for all portions of the operating system such as memory management, process management, tile management, as well as input/output management.
Due to the fact that all user applications rely on the integrity of the kernel and core system utilities, the compromise of any one portion of the system may result in a complete lack of reliability in the system as a whole. Particularly in the case of commodity operating systems, the ability to place assurance on the numerous and complex portions of the system has been found to be exceedingly difficult. The most important pieces of this complex system reside in the core of the kernel itself. While a variety of tools and architectures have been developed for the protection of kernel integrity on commodity systems, most have a fundamental flaw—they rely on some portion of kernel correctness to remain trustworthy themselves. In a world of increasingly sophisticated attackers this assumption is frequently invalid.
There is therefore a growing need for integrity protection which does not rely on kernel or other system resources correctness and which is able to effectively detect modifications to the kernel including the presence of rootkits.
Rootkits are collections of programs that enable attackers who have gained administrative control of a host to modify the host's software, usually causing it to hide their presence from the host's genuine administrators. Currently, there are twelve commonly used publicly known rootkits. Every popular rootkit soon encourages the development of a program designed to detect it. Every new detection program inspires rootkit authors to find better ways to hide. However, in this race, the rootkit designers have traditionally held the advantage: the most sophisticated rootkits modify the operating system kernel of the compromised host to secretly work on behalf of the attacker. When an attacker can arbitrarily change the functionality of the kernel, no user program that runs on the system can be trusted to produce correct results which includes user programs for detecting rootkits.
TABLE 1rootkitloadsoverwritesadds newmodifiesadds hookadds inetnameviasyscall jumpsyscall jumpkernel textto/procprotocolComplete rootkits:adore 0.42LKMxknark 2.4.3LKMxxxrialLKMxrkit 1.01LKMxSucKIT 1.3bkmemxxsynapsys 0.4LKMxDemonstrates module or process hiding only:modhidelLKMxphantasmagoriaLKMxphideLKMxDemonstrates privilege escalation backdoor only:kbd 3.0LKMxtaskigtLKMxDemonstrates key logging only:Linspy v2beta2LKMx
Rootkits can be partitioned into two classes: those that modify the host operating system kernel and those that do not. Those in the second class are simpler and easier to detect. These simple rootkits replace critical system utilities such as ps. ls. and netstat with modified versions that deceptively fail to report the presence of an attacker's processes, files, and network connections.
However, since the operating system kernel is not part of the deception, a suspicious administrator can detect the presence of these simple rootkits by comparing the output of the modified utilities against the output of unmodified versions obtained from read-only installation media or against information provided directly by the operating system kernel via the /proc files system (D. Brumley, Invisible Intruders: Rootkits in Practice; login: USENIX and SAGE, September, 1999). Additionally, defensive software is available which monitors the files containing a host's critical system utilities for the kinds of unexpected changes caused by a simple rootkit installation. (G. H. Kim and E. H. Spafford. The Design and Implemention of Tripwire: A File System Integrity Checker. In Proceedings of the 2nd ACM Conference on Computer and Communications Security, Pages 18-19, Fairfax, Va., November 1999; and J. Molina and W. A. Arbaugh. Using Independent Auditors as Intrusion Detections Systems in Proceedings of the 4th International Conference on Information and Communications Security, pp. 291-302, Singapore, December 2002.)
There is a continuing and ongoing race between developers of complex rootkit detection tools that depend on at least some part of the kernel remaining unmodified and developers of rootkits who respond by increasing the scope of their modifications.
Therefore, kernels and other critical system utility monitors are needed which are designed to detect the more complex class of rootkits that modify the host operating system kernel which provide deceptive information. This monitor demonstrates a means of escaping this race by making its software independent on the health of the host operating system kernel being monitored and isolating it from the reach of an attacker. Known detection techniques fail when run on a sufficiently modified kernel such that when the kernel itself is providing incorrect information even correct system utilities can do nothing but pass this false information on to the user.
The example rootkits in Table 1 (Features of example Linux or kernel-modifying rootkits) provide a variety of services. Nearly all are designed to make the kernel return incorrect or incomplete information when queried by user-mode programs in order to hide the presence of the rootkit and the attacker's processes and files. Some of them also provide backdoors allowing remote access to the compromised system or provide a means of privilege escalation to local users with some of the example rootkits providing keystroke loggers.
The rootkits in the “complete rootkits” section of Table 1 provide a sufficient amount of deceptive functionality that they might be of use to an actual attacker. The remaining rootkits provide only limited functionality and serve only to demonstrate how a particular aspect of rootkit functionality may be implemented.
The check boxes in the Table 1 identify the means by which attackers load the rootkits into the kernel and the means by which the rootkits cause the kernel to execute their functions.
It is known to those skilled in computer arts' that monolithic kernels which have traditionally been used by Unix and Linux contain all the operating system core functions and the device drivers (from all programs that allow the operating system to interact with hardware devices). Modern monolithic kernels feature the ability to load modules at run-time thus allowing easy extension of the kernel's capabilities as required.
The column “loads via” of Table 1 shows that all but one of the example rootkits are implemented as LKMs (Loadable Kernel Modules) and are designed to be loaded through the kernel's LKM-loading interface as if they were device drivers. This fact is significant since an unmodified kernel will report the presence of all loaded LKMs. A stealthy rootkit must take means to modify the kernel or its LKM management data structures to avoid being revealed in these reports. The LKM-loading interface is not the only means of loading a rootkit into the kernel. The SucKIT rootkit is designed to be written into kernel memory via the kernel's /dev/kmem interface using a user-mode loading program provided with the rootkit. (The /dev/kmem interface provides privileged processes with direct access to kernel memory as if it were a file.) This loading method does not use the kernel's LKM-loading interface and consequently leaves no trace in its data structures.
The remaining columns of Table 1 show that the example rootkits use a variety of means to cause the kernel to execute their code. Nearly all of them overwrite the addresses of some of the kernel's system call handling functions in the system call table with the addresses of their own doctored system call handling functions. System calls is a specified set of program interfaces through which application programs or other parts of the operating system request the kernel's services. The act of system call interposition causes the kernel to execute the rootkit's doctored system call handling functions rather than its own when a user program makes a system call.
The rootkit's doctored functions may implement deceptive functionality in addition to the service normally provided by the system call. For example, rootkits often interpose on the fork system call so that they may modify the kernel's process table data structure in a manner which prevents an attacker's processes from appearing in the user-visible process list whenever the kernel creates a new process. Privilege-escalating backdoors are also common: the rkit rootkit's doctored setuid function resets the user and group identity of processes owned by an unprivileged attacker to those of the maximally-privileged root user.
System call interposition is not the only means by which rootkits cause the kernel to execute their functions. In addition to interposing on existing system calls, the “SucKIT” rootkit adds new system calls into previously empty slots in the kernel's system call table. The “phantasmagoria” rootkit avoids the system call table altogether and modifies the machine instructions at the beginnings of several kernel functions to include jumps to its own functions. The “knark” and “taskigt” rootkits add hooks to the /proc filesystem that cause their functions to be executed when a user program reads from certain /proc entries. The “taskigt” rootkit, for example, provides a hook that grants the root user and group identity to any process that reads a particular /proc entry. The “knark” rootkit also registers its own inet protocol handler which causes the kernel to execute a privileged process running an arbitrary program when the kernel receives certain kinds of network packets.
Existing Detection Software
A number of tools designed to detect kernel modifying rootkits are currently available to system administrators. These software packages make a series of checks on any number of system resources to determine if that system is in an anomalous state. The kernel performs its tasks (e.g., executing processes and handling interrupts) in “kernel space” or kernel memory, whereas everything a user normally does (e.g., writing text in a text editor or running graphical programs) is done in “user space”. This separation is made in order to prevent user data and kernel data from interfering with each other and thereby diminishing performance or causing the system to become unstable.
There are generally two categories of kernel-modifying rootkit detectors: those that check for rootkit symptoms by looking for discrepancies that are detectable from user-space and those that analyze kernel memory directly to detect changes or inconsistencies in kernel text and/or data structures. These two types of tools will be referred further herein as “user-space” and “kernel memory” tools respectively. A number of tools can be considered both user-space and kernel memory tools, as they provide detection mechanisms that fall into both categories. Table 2 (kernel-modifying rootkit detector mechanisms) summarizes a representative sample of commonly used rootkit detectors that are able at least to some degree, detect kernel-modifying rootkits. Those tools with an “x” present in the column entitled “User-Space System Detection” perform user-space checks. Those with marks in either of the two leftmost columns analyze kernel memory through specified mechanisms.
Many kernel-modifying rootkits have symptoms that are readily-observable from user-space without accessing kernel memory or data structures directly. For example, as previously described some rootkits add entries of the kernel's /proc files system. Such entries can often be found with standard directory lookups and, many times, even with trusted and non-compromised versions of ls. Similarly, a number of LKM rootkits do a poor job of hiding themselves from simple user queries such as checking for exported symbols in /proc/ksyms. These symbols are part of the rootkit's added kernel text and do not exist in healthy kernels.
TABLE 2Kernel memory accessrootkitdetectorsynchronoususer-spacedetector:/dev/kmemLKMdetectionsymptom detectionKSTATxxxSt. MichaelxxCarbonitexSamhainxxChkrootkitxcheckpsxRkscanxRootCheckxRootkitxHunter
User-space checks fall into two categories, either rootkit-specific or non-rootkit specific. The former are efficient at detecting well-known rootkits using simple checks for specific directories, files, kernel symbols, or other attributes of the system. One of the most common rootkit-specific detectors, “chkrootkit”, has a set of predetermined tests it performs looking for these attributes and can detect LKM rootkits currently in common use.
Non-rootkit specific checks by user-space tools generally perform two types of tasks. The first task is a simple comparison between information provided through the /proc files system and the same information as determined by system calls or system utilities. One such common check is for process directory entries hidden from ps and the readdir system call. The second common user-space check is for anomalies in the Linux virtual filesystem directory structure. Some rootkits hide directories, resulting in potential discrepancies between parent-directory link counts and the number of actual subdirectories visible by user programs.
While user-space checks may prove useful under certain conditions they generally have two fundamental limitations. The first limitation is due to the fact that they are dependent on interfaces provided by the kernel and the most critical of compromises can be concealed with an advanced kernel-resident rootkit. The second limitation is that most of the symptoms that are detectable from user-space are not general enough to protect against new and unknown rootkits. However, there is a set of tools whose purpose is to protect the kernel in a more general way which is accomplished by watching for rootkits at the point of attack in kernel memory. The mechanism used by these tools to access kernel memory and the short-comings with each approach, and some general insight into the types of checks kernel memory protectors perform, as well as four common tools currently used to detect rootkits using kernel memory will be presented infra herein.
The methods available to rootkit detectors are not unlike those utilized by rootkits themselves. Unfortunately, easy access to kernel memory is a double-edged sword. Although it provides for the convenient extensibility of the Linux kernel through kernel modules, it also provides for the trivial insertion of new kernel code by attackers who have gained root privileges. The two primary access paths to the Linux kernel (LKM-loading interface and the /dev/kmem interface) were described supra in relation to Table 1. The /dev/kmem, allows attackers and protectors alike to write user programs that can arbitrarily change kernel virtual memory. There is much more overhead involved with a program that uses /dev/kmem, since symbols must be ascertained independently (typically from /proc/ksyms or the System.map file) and data structures generally must be processed manually. However, the portability of a tool written in this manner would allow it to work even on kernels built without LKM support. One major drawback which must be considered by authors of tools that use /dev/kmem is that the interface is provided by the kernel, which is the entity whose integrity they seek to verify. Because the interface is provided by a kernel driver, there is always the potential that a rootkit is providing false information to the user-space tool.
Another method, insertion of an LKM by the tool, may be a far more powerful approach. First, it gives the tool the ability to execute code in the kernel, the privileges of which include the ability to manipulate the scheduler, utilize kernel functions, provide additional interfaces to user-space programs, and have immediate access to kernel symbols. The negatives of using an LKM are twofold. First, the approach clearly will not work in a kernel built without LKM support. Secondly, a rootkit already resident in the kernel could modify, replace, or ignore a module as it sees fit, depending on its sophistication.
Once provided access to kernel memory, tools take a number of approaches to protecting the kernel. First, and perhaps the most well-known, is protection of the system call table described in R. J. Jones, “Loadable Kernel Modules”, login: The magazine of USENIX and SAGE, 26(7), November 2001. As shown in Table 1, the vast majority of rootkits utilize system call interposition in one form or another. Rootkit detectors with access to system memory can perform a number of checks on the system call table, the most notable of which is storing a copy or partial copy of the table and the functions to which it points. This copy is then used at a later time to make periodic checks of the table. A similar procedure is also used by some kernel memory-based detectors to check the interrupt descriptor table (IDT), and in one case the entire kernel text.
In addition to protecting text and jump tables within the kernel, detection tools are used to provide information about kernel data that cannot easily be obtained from user-space. Some common examples are the data structures associated with LKMs, files, sockets, and processes, each of which can change frequently in a running kernel. Tools with access to kernel memory can parse and analyze this data in order to look for suspicious or anomalous instances of these objects. User-space tools that use /dev/kmem and LKMs that create new interfaces may compare data obtained directly from the kernel in order to find hidden processes, files, sockets, or LKMs.
Table 3 (kernel-modifying rootkit detector functionality) provides a list of four common kernel memory-based rootkit detection tools. The Table 3 also shows a set of functionality that is common among such detectors, as well as the specific functionality provided by each tool. Each of these four tools examines kernel memory and is briefly described infra herein.
TABLE 3St.FunctionalityKSTATMichaelCarboniteSamhainLong-term change detectionHidden LKM detectionxxSyscall table change detectionxxxSyscall function changexxxdetectionKernel text modificationxdetectionIDT change detectionxxShort-term system stateHidden process detectionxxHidden socket detectionxxExtra featuresHides self from rootkitsxRestore modified text changesx
KSTAT is a tool for system administrators, used to detect changes to the interrupt descriptor table (IDT), system call vector, system call functions, common networking functions, and proc filesystem functions. Additionally, it provides an interface for obtaining information about open sockets, loaded kernel modules, and running processes directly from kernel memory. KSTAT relies on /dev/kmem for its checking, but uses LKMs in two ways. First, the initial run of KSTAT on a “clean” kernel uses a module to obtain kernel virtual memory addresses for some of the networking and filesystem functions it protects. Second, due to the fact that the module list head pointer is not exported by the Linux kernel, a “null” module is used to locate the beginning of the module linked list at each check for new modules. Change detection is performed by using “fingerprints” of the original versions. In the case of function protection, this amounts to the copying of a user-defined number of bytes at the beginning of the function. Jump tables (e.g., IDT and system call) are copied in full.
Another tool that uses /dev/kmem for kernel integrity protection is Samhain, a host-based intrusion detection system (IDS) for Unix/Linux. While rootkit detection is not the only function of Samhain, the tool provides IDT, system call table, and system call function protection similar to that of KSTAT. Although it does not perform all of the functionality with regard to kernel state provided by KSTAT, Samhain does have one additional feature, which is the ability to hide itself. An LKM can be loaded to hide process and file information for Samhain so that an attacker might not notice the tool's existence when preparing to install a rootkit. Because of this feature, administrators can prevent attackers with root access from recognizing and killing the Samhain process.
St. Michael tool is likely the most well-known rootkit detector tool available. As part of the St. Jude kernel IDS system, St. Michael attempts to protect kernel text and data from within the kernel itself via an LKM. St. Michael provides most of the same protection as KSTAT and Samhain with a number of added features. First, it replaces copy fingerprints with MD5 or SHA-1 hashes of kernel text and data structures, thereby covering larger segments of that information. Second, St. Michael is the only tool discussed that provides both preventative and reactive measures for kernel modification in addition to its detection features. The former are provided through changes such as turning off write privileges to /dev/kmem and performing system call interposition on kernel module functions in order to synchronously monitor kernel changes for damage. Because of its synchronous nature, St. Michael has a distinct advantage in detection time, to the point that it can actually prevent changes in some cases. The final major advantage of the St. Michael system is its ability to restore certain parts of kernel memory in the case of a detected change. By backing up copies of kernel text, St. Michael provides an opportunity to replace modified code before an attacker utilizes changes made by a rootkit. However, St. Michael has the same disadvantages as any LKM-based tool, as described previously.
Another LKM-based memory, known as Carbonite traces all tasks in the kernel's task list and outputs diagnostic information such as open files, open sockets, environment variables, and arguments. An administrator can then manually audit the output file in search of anomalous entries. Carbonite is a good example of a tool that can be used to produce more specific information after an initial indication of intrusion.
All these Detection tools however disadvantageously depend to a certain degree on the health of the monitored kernel, and therefore may fail to detect malicious modifications if the host's system has been seriously compromised.